#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "admin.h"
#include "url_info.h"
#include "stat.h"
#include "configure.h"
	
#define ADMIN_PORT 8080
extern void send_404(int sock);
extern struct cache_stat *cache_stat;
	
struct action
{
	char file[1500];
	void (*action)(struct url_info *url_info, int sock);
};

int emit_conf(char *dst)
{
	char *cur = dst;
	struct conf conf;

	read_config_file_simple(&conf);

	cur += sprintf(cur, "<table border=\"1\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td width=\"150\">root dir </td>");
	cur += sprintf(cur, "<td >%s </td>", conf.buff);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td>map </td>");
	cur += sprintf(cur, "<td>%s </td>", conf.buff_map);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td>use map </td>");
	cur += sprintf(cur, "<td>%s </td>", conf.buff_use_map);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td>additional header </td>");
	cur += sprintf(cur, "<td>%s </td>", conf.buff2);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td>content type </td>");
	cur += sprintf(cur, "<td>%s </td>", conf.buffcontyp);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td>omit options </td>");
	cur += sprintf(cur, "<td>%s </td>", conf.buffkey);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td>prefix </td>");
	cur += sprintf(cur, "<td>%s </td>", conf.buffprefix);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td>disk space </td>");
	cur += sprintf(cur, "<td>%s </td>", conf.buff_disk_space);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td>day </td>");
	cur += sprintf(cur, "<td>%s </td>", conf.buff_day);
	cur += sprintf(cur, "</tr>");

	cur += sprintf(cur, "</table>");

	return cur-dst;
}

int emit_clean(char *dst)
{
	char *cur = dst;

	stat_lock(cache_stat);

	cur += sprintf(cur, "total checked: %d total cleaned: %d<br>", cache_stat->check, cache_stat->clean);
	cur += sprintf(cur, "last checked: %s<br>", cache_stat->lastcheck);
	cur += sprintf(cur, "last cleaned: %s", cache_stat->lastclean);

	stat_unlock(cache_stat);
	return cur - dst;

}
int emit_download(char *dst)
{
	int i, j, k;
	char *cur = dst;

	stat_lock(cache_stat);

	cur += sprintf(cur, "download success: %d download fail: %d hit(success): %d hit(404): %d remove(scrap): %d", 
			cache_stat->success, cache_stat->fail, cache_stat->hit_success, 
			cache_stat->hit_404, cache_stat->remove_scrap);
	cur += sprintf(cur, "<table border=\"1\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">");

	cur += sprintf(cur, "<tr>");
	cur += sprintf(cur, "<td width=\"15\">D</td>");
	cur += sprintf(cur, "<td width=\"40\">size(k)</td>");
	cur += sprintf(cur, "<td width=\"40\">time</td>");
	cur += sprintf(cur, "<td >url</td>");
	cur += sprintf(cur, "</tr>");

	for (i=0, j=cache_stat->index+MAX_ENTRY-1; i<MAX_ENTRY; i++, j--)
	{
		k = j % MAX_ENTRY;
		cur += sprintf(cur, "<tr>");
		cur += sprintf(cur, "<td>%s</td>", cache_stat->item[k].success ? "+" : "-");
		cur += sprintf(cur, "<td>%d</td>", cache_stat->item[k].size/1024);
		cur += sprintf(cur, "<td>%d</td>", cache_stat->item[k].time);
		cur += sprintf(cur, "<td>%s</td>", cache_stat->item[k].url);
		cur += sprintf(cur, "</tr>");
	}

	cur += sprintf(cur, "</table>");
	stat_unlock(cache_stat);

	return cur-dst;
}

int emit(char *dst, char *src)
{
	if (strcmp(src, "conf_table") == 0)
		return emit_conf(dst);
	else if (strcmp(src, "download_table") == 0)
		return emit_download(dst);
	else if (strcmp(src, "clean_info") == 0)
		return emit_clean(dst);
	return 0;
}

void cat_action(struct url_info *url_info, int sock)
{
	struct stat st;
	int fd;
	char *pathname = "/usr/local/http/web/info.html";
	char buff_old[1<<19];
	char buff_new[1<<20];
	char buff_fin[1<<20];
	char *cur_old = buff_old;
	char *cur_new = buff_new;
	char *cur_fin = buff_fin;
	char *loc1, *loc2;

	if ((fd = open(pathname, O_RDONLY)) < 0)
	{
		send_404(sock);
		goto out;
	}

	if (fstat(fd, &st) < 0)
	{
		send_404(sock);
		goto out;
	}

	if (read(fd, buff_old, st.st_size) != st.st_size)
	{
		send_404(sock);
		goto out;
	}

	buff_old[st.st_size] = 0;

	while (loc1 = strstr(cur_old, "<%"))
	{
		memcpy(cur_new, cur_old, loc1-cur_old);
		cur_new += loc1 - cur_old;
		loc2 = strstr(cur_old, "%>");
		*loc2 = 0;
		cur_new += emit(cur_new, loc1+2);
		cur_old = loc2 + 2;
	}

	strcpy(cur_new, cur_old);

	cur_fin += sprintf(cur_fin, "HTTP/1.1 200 Ok\r\n");
	cur_fin += sprintf(cur_fin, "Content-length: %d\r\n", strlen(buff_new));
	cur_fin += sprintf(cur_fin, "Content-Type: text/html;charset=gb2312\r\n");
	cur_fin += sprintf(cur_fin, "\r\n");
	cur_fin += sprintf(cur_fin, "%s", buff_new);

	send(sock, buff_fin, cur_fin - buff_fin, 0);

out:
	close(sock);
	if (fd >= 0)
		close(fd);
}

void set_action(struct url_info *url_info, int sock)
{
	close(sock);
}

void def_action(struct url_info *url_info, int sock)
{
	close(sock);
}

int maplen = 2;
struct action map[] = { 
	{ "catinfo", cat_action },
	{ "setting", set_action } };

int lookup_action(char *filename)
{
	int i;

	for (i=0; i<maplen; i++)
		if (strcmp(map[i].file, filename) == 0)
			return i;
	return -1;
}
void serve_one_client(int sock)
{
	int index;
	struct url_info url;
	struct timeval tvsnd = {10,0}; 
	struct timeval tvrcv = {30,0}; 

	if (setsockopt(sock, SOL_SOCKET,SO_SNDTIMEO, (char *)&tvsnd,sizeof(struct timeval)) < 0)
		perror("setsockopt");
	
	if (setsockopt(sock, SOL_SOCKET,SO_RCVTIMEO, (char *)&tvrcv,sizeof(struct timeval)) < 0)
		perror("setsockopt");

	if (recv_header(sock, &url) < 0)
	{
		send_404(sock);
		goto out;
	}

	if (process_header_line(&url) < 0)
	{
		send_404(sock);
		goto out;
	}

	if (locate_filename_and_qmark(&url) < 0)
	{
		send_404(sock);
		goto out;
	}

	if (url.qmark)
		*url.qmark = 0;

	if (!url.filename)
		cat_action(&url, sock);
	else if ((index = lookup_action(url.filename)) >= 0)
		map[index].action(&url, sock);
	else
		def_action(&url, sock);

	return;
	
out:
	close(sock);
	return;

}

int admin_process()
{
	int sock_lsn;
	int sock_peer;
	struct sockaddr_in addr_lsn;

	memset(&addr_lsn, 0, sizeof(addr_lsn));
	addr_lsn.sin_family = AF_INET;
	addr_lsn.sin_port = htons(ADMIN_PORT);
	addr_lsn.sin_addr.s_addr = htonl(INADDR_ANY);
	
	if ((sock_lsn = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("socket() error\n");
		return 0;
	}
	if (bind(sock_lsn, (struct sockaddr*)&addr_lsn, (socklen_t)sizeof(addr_lsn)) < 0)
	{
		printf("bind() fail\n");
		return 0;
	}

	if (listen(sock_lsn, 20) < 0)
	{
		printf("listen error\n");
		return 0;
	}
	while (1)
	{
		if ((sock_peer = accept(sock_lsn, 0, 0)) < 0)
		{
			sleep(2);
			continue;
		}
		if (fork() == 0)
		{
			close(sock_lsn);
			serve_one_client(sock_peer);
			_exit(0);
		}
		close(sock_peer);
	}

}

void start_admin()
{
	if (fork() == 0)
	{
		admin_process();
		_exit(0);
	}
}
